#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAX_URL_LEN 200
#define MAX_PATH_LEN 100
#define MAX_TASK_CNT 10
#define MSG_DATA_LEN 128

typedef struct {
  void **items;
  int item_maxcount;
  int item_count;
  int item_size;
} _Z_SLIST;

typedef void *Z_SLIST;

typedef struct _DOWNLOAD_TASK {
  char url[MAX_URL_LEN];
  char save_path[MAX_PATH_LEN];
} _download_task;

typedef enum { PRIO_NORMAL, PRIO_HIGH } DOWNLOAD_PRIO;

pthread_t tid = 0;

static Z_SLIST task_slist = NULL;
static pthread_mutex_t task_slist_mutex;

int task_slist_insert(Z_SLIST list, const void *item, int idx); //插入下载服务
Z_SLIST task_slist_new(int item_size, int item_maxcount); // 创建下载队列函数
int task_slist_delete(Z_SLIST list, int idx);       //删除下载队列函数
int task_slist_add(Z_SLIST list, const void *item); // 添加下载队列函数
void *task_slist_get(Z_SLIST list, int idx);        //获得消息队列函数
int task_add(char *url, char *save_path,
             DOWNLOAD_PRIO prio); //将下载路径加入下载队列
void *download_thread(void *arg); // 实际下载线程函数
int init_service();               //下载服务初始化函数
int deinit_service();             //下载服务反初始化函数

int task_slist_insert(Z_SLIST list, const void *item, int idx) {
  _Z_SLIST *l = (_Z_SLIST *)list;

  if (l->item_count >= l->item_maxcount) {
    return -1;
  }

  if (idx >= 0 && idx <= l->item_maxcount) {
    int i = 0;
    for (i = l->item_count; i > idx; i--) {
      memcpy((char *)l->items + i * l->item_size,
             (char *)l->items + (i - 1) * l->item_size, (int)l->item_size);
    }
    memcpy((char *)l->items + idx * l->item_size, item, (int)l->item_size);
    l->item_count++;
  }
  return idx;
}

Z_SLIST task_slist_new(int item_size, int item_maxcount) {
  _Z_SLIST *list = NULL;
  int itemstotalsize = item_size * item_maxcount;

  list = (_Z_SLIST *)malloc(sizeof(_Z_SLIST));
  list->items = (void **)malloc(itemstotalsize);

  list->item_count = 0;
  list->item_maxcount = item_maxcount;
  list->item_size = item_size;

  return (Z_SLIST)list;
}

int task_add(char *url, char *save_path, DOWNLOAD_PRIO prio) {
  // printf("task add %s\n", url);

  int ret = -1;

  _download_task task = {0};
  strncpy(task.url, url, sizeof(task.url));
  strncpy(task.save_path, save_path, sizeof(task.save_path));

  // printf("task add %s\n", url);

  pthread_mutex_lock(&task_slist_mutex);

  if (prio == PRIO_HIGH) {
    ret = task_slist_insert(task_slist, &task, 0);
  } else {
    ret = task_slist_add(task_slist, &task);
  }

  pthread_mutex_unlock(&task_slist_mutex);

  printf("task add finish.\n");

  return ret;
  // return task_slist_add(task_slist, &task);
}

int task_slist_add(Z_SLIST list, const void *item) {
  _Z_SLIST *l = (_Z_SLIST *)list;

  if (l->item_count >= l->item_maxcount) {
    return -1;
  }

  if (l->item_count < l->item_maxcount) {
    memcpy((char *)l->items + l->item_count * l->item_size, item,
           (int)l->item_size);
    l->item_count++;
  }

  return 0;
}

void *task_slist_get(Z_SLIST list, int idx) {
  _Z_SLIST *l = (_Z_SLIST *)list;

  if (!(idx >= 0 && idx < l->item_count)) {
    return NULL;
  }

  return (void *)((char *)l->items + idx * l->item_size);
}

int task_slist_delete(Z_SLIST list, int idx) {
  _Z_SLIST *l = (_Z_SLIST *)list;

  if (idx >= 0 && idx < l->item_count) {
    int i = 0;

    for (i = idx; i < l->item_count - 1; i++) {
      memcpy((char *)l->items + i * l->item_size,
             (char *)l->items + (i + 1) * l->item_size, (int)l->item_size);
    }

    l->item_count--;
  }

  return 0;
}

void *download_thread(void *arg) {
  _download_task *task = NULL;

  while (1) {
    pthread_mutex_lock(&task_slist_mutex);
    task = (_download_task *)task_slist_get(task_slist, 0);

    if (task != NULL) {
      printf("New download task:\n");
      printf("Downloadint ... url=%s\n", task->url);

      if (vfork() == 0) {
        execlp("wget", "wget", "-P", task->save_path, "-t", "2",
               "--connect-time", "3", task->url, NULL);
      }

      sleep(1);
      printf("Finish! save_path=%s\n\n", task->save_path);
      task_slist_delete(task_slist, 0);
    }

    pthread_mutex_unlock(&task_slist_mutex);

    sleep(1);
  }

  return NULL;
}

int init_service() {
  task_slist = task_slist_new(sizeof(_download_task), MAX_TASK_CNT);
  pthread_create(&tid, NULL, download_thread, NULL);
  return tid;
}

int deinit_service() {

  int ret = pthread_cancel(tid); //发送终止信号给tid线程

  if (0 == ret) {
    pthread_join(tid, NULL); // 等待tid线程退出
  }

  return ret;
}

int main() {
  if (pthread_mutex_init(&task_slist_mutex, NULL) != 0) {
    printf("Init mutex error.\n");
    exit(-1);
  }

  init_service(); //初始化下载服务

  task_add("http://39.108.177.3:8080/images/bill.jpg", "./path", PRIO_NORMAL);
  // task_add("http://video2", "./video2", PRIO_NORMAL);

  sleep(2);

  task_add("https://static.oschina.net/uploads/img/201306/17131329_1lP6.jpg",
           "./path", PRIO_HIGH);

  // task_add("http://video3", "./video3", PRIO_HIGH);

  printf("download task add finished!\n");

  sleep(5);

  // deinit_service(); // 反初始化下载服务

  pthread_join(tid, NULL); // 等待tid线程退出

  pthread_mutex_destroy(&task_slist_mutex);

  return 0;
}
